using Beaker.OpenCube.Render.Maths;
using System;
using System.Diagnostics.Contracts;
using Beaker.OpenCube.Render.Materials;

namespace Beaker.OpenCube.Render.Shapes
{
	public sealed class Sphere
		: Shape
	{
		private readonly Point3 _center;
		private readonly double _radius;
		private readonly double _radiusSquared;
		private readonly IMaterial _material;

		public Sphere( Point3 center, double radius, IMaterial material )
		{
			_center = center;
			_radius = radius;
			_radiusSquared = _radius * radius;
			_material = material;
		}

		/// <summary>
		/// Computes the intersection with the given ray.
		/// </summary>
		/// <param name="scene">The scene.</param>
		/// <param name="ray">The ray to compute an intersection with.</param>
		/// <returns>
		/// An intersection or <c>null</c> when there was no intersection.
		/// </returns>
		public override Intersection ComputeIntersection( Scene scene, Ray ray )
		{
			var eo = _center - ray.Origin;
			var v = eo * ray.Direction;
			double distance = 0d;
			if( v >= 0 )
			{
				var disc = _radiusSquared - ( ( eo * eo ) - ( v * v ) );
				if( disc >= 0d )
					distance = v - Math.Sqrt( disc );
			}
			if( distance.AboutZero() )
				return null;

			Point3 point = ray.Origin + ray.Direction * distance;
			return new Intersection( scene, this, ray, distance, ( point - _center ).Normalize(), _material );
		}
	}
}